Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IEP-1265: Debug Process Hangs #1023

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open

IEP-1265: Debug Process Hangs #1023

wants to merge 7 commits into from

Conversation

alirana01
Copy link
Collaborator

@alirana01 alirana01 commented Jul 22, 2024

Description

The port handling logic was flawed added proper code to verify and assign ports with a logic for retries as well.
Also changed the debugger tab so that the gdb client controls are only enabled when the openOCD server is not started from the IDE.

Fixes # (IEP-1265)

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)

How has this been tested?

Try debugging on mac os and verify that openocd is able to connect. Also start openocd only from cli and try to connect debugger

Test Configuration:

  • ESP-IDF Version: all
  • OS (Windows,Linux and macOS): any

Checklist

  • PR Self Reviewed
  • Applied Code formatting
  • Verified on all platforms - Windows,Linux and macOS

Summary by CodeRabbit

Summary by CodeRabbit

  • Bug Fixes

    • Disabled certain debugging capabilities by commenting out process and console factory extensions, which may improve stability.
    • Adjusted how the launch configuration handles port assignments, ensuring ports are only set when necessary.
    • Enhanced port availability checking for improved reliability and logging.
  • New Features

    • Introduced a new user interface component for GDB client controls, improving organization and user experience.
    • Updated logic for GDB server configuration to simplify port retrieval, enhancing usability.

@alirana01 alirana01 self-assigned this Jul 22, 2024
Copy link

coderabbitai bot commented Jul 22, 2024

Walkthrough

The recent modifications involve commenting out an extension block in the plugin.xml for the com.espressif.idf.debug.gdbjtag.openocd plugin, disabling certain debugging functionalities. Additionally, the Launch.java file has been updated to conditionally start the GDB server before assigning a remote port, enhancing control over port management. Furthermore, the PortChecker class has been improved to check port availability more effectively. The user interface in TabDebugger.java has also been organized with a new group for GDB client controls.

Changes

File Path Change Summary
bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml Commented out the <extension> block for process and console factories, disabling their functionality.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java Added a conditional check to start the GDB server before assigning an available remote port.
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java Enhanced isPortAvailable method to check port availability using a ServerSocket.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java Introduced a new gdbClientGroup for better organization of GDB client controls in the UI.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java Modified the getGdbServerCommandLineArray method to simplify port retrieval using default values.

Possibly related PRs

  • fix: Address issue with the cdt-lsp-2.0.0 #1021: The changes in the plugin.xml and Launch.java files in the main PR relate to the handling of GDB server configurations, which may be impacted by the bug fix in the retrieved PR that addresses issues with the LSP editor and clangd preferences, as both involve debugging and configuration management within the Eclipse environment.

Poem

🐇 In the land of code where rabbits play,
Changes hop in, brightening the day.
With extensions silenced, a new path is spun,
Debugging adventures just waiting for fun!
With a twitch of my nose, I’ll bound through the flow,
Celebrating adjustments, let the processes glow! ✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Outside diff range, codebase verification and nitpick comments (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (1)

Verify the impact of disabling process and console factories.

The <extension> block related to processFactory and consoleFactory has been commented out, which temporarily disables these debugging functionalities. The search results indicate that CustomIdfProcessFactory and IdfProcessConsoleFactory are referenced in several Java files, suggesting dependencies on these classes. Commenting out these extensions could potentially break functionality related to process creation and console management in the debugging environment.

  • Files with references:
    • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java
    • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/CustomIdfProcessFactory.java
    • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/console/IdfProcessConsoleFactory.java
    • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/process/StreamListener.java

Further verification or testing is recommended to ensure that the debugging capabilities are not adversely affected by these changes.

Analysis chain

Line range hint 155-168:
Verify the impact of disabling process and console factories.

The <extension> block related to processFactory and consoleFactory has been commented out, which temporarily disables these debugging functionalities. Ensure that this change does not adversely affect the debugging capabilities required for your development workflow.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the impact of commenting out the process and console factories.

# Test: Search for the usage of `CustomIdfProcessFactory` and `IdfProcessConsoleFactory`. Expect: No critical dependencies.
rg --type xml -A 5 $'CustomIdfProcessFactory'
rg --type xml -A 5 $'IdfProcessConsoleFactory'

Length of output: 1306


Script:

#!/bin/bash
# Description: Verify the impact of commenting out the process and console factories in Java files.

# Test: Search for the usage of `CustomIdfProcessFactory` and `IdfProcessConsoleFactory` in Java files. Expect: No critical dependencies.
rg --type java -A 5 $'CustomIdfProcessFactory'
rg --type java -A 5 $'IdfProcessConsoleFactory'

Length of output: 6195

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between e97546f and c5803ff.

Files selected for processing (2)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (2 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java

@kolipakakondal
Copy link
Collaborator

@alirana01 Having the same issue with the PR as well.
Screenshot 2024-07-26 at 5 03 20 PM

stack.log

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between c5803ff and 560de09.

Files selected for processing (2)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml (2 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Files skipped from review due to trivial changes (1)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/plugin.xml
Additional comments not posted (1)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

154-154: Potential Impact of Commenting Out Process Factory ID Setting

Commenting out the line that sets the DebugPlugin.ATTR_PROCESS_FACTORY_ID could significantly alter how processes are managed during debugging. This change is likely an attempt to troubleshoot the hanging issue, but it's crucial to verify that this does not introduce other issues in the debugging process.

Please ensure to test this change across all supported platforms to confirm that it resolves the hanging issue without adverse effects. Consider re-enabling this line conditionally based on further findings or implementing a more targeted fix if this proves to be too broad.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 560de09 and 5d4bfad.

Files selected for processing (2)
  • bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
Additional comments not posted (2)
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)

28-66: The retry mechanism, logging, and exception handling are well-implemented.

The retry mechanism is a good addition to handle transient issues that may cause temporary port unavailability. The maximum number of retries (3) and the delay between retries (200ms) seem reasonable. The method correctly returns false if the port is still not available after exhausting all retries.

The logging statements provide useful information for debugging and monitoring purposes. The log messages are clear and informative.

The method handles InterruptedException during the sleep period by restoring the interrupt status and logging an appropriate message. This ensures that the method behaves correctly when interrupted and doesn't silently swallow the exception.

bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

150-155: LGTM!

The conditional check is a good addition to ensure that the port number is only set when the GDB server is intended to be started. This change prevents unnecessary port assignments and enhances the control flow.

}
return true; //Fallback not reachable
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the unreachable fallback return statement.

The fallback return statement return true; at line 66 is unreachable. This statement serves no purpose and can be removed.

Apply this diff to remove the unreachable fallback return statement:

-		return true; //Fallback not reachable
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return true; //Fallback not reachable

@alirana01 alirana01 changed the title [WIP] IEP-1265: Debug Process Hangs IEP-1265: Debug Process Hangs Sep 16, 2024
Copy link
Collaborator Author

@alirana01 alirana01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Self Review

Copy link
Collaborator

@sigmaaa sigmaaa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@kolipakakondal
Copy link
Collaborator

This didn't work for me. Need to check further.

Please find the attached IDE error log.
ide.log

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (3)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

Line range hint 1-265: Summary of review and areas for further attention

The changes in this file address the port handling logic as mentioned in the PR objectives. However, there are a few areas that might need further attention:

  1. The PR objectives mention debugging process hangs, but the changes in this file don't directly address this issue. Could you please clarify how these changes are expected to resolve the process hangs?

  2. The PR comments indicate that a user (kolipakakondal) is still experiencing issues. It might be beneficial to review the attached log files ("stack.log" and "ide.log") to see if they provide any insights into persisting problems.

  3. Consider adding more comprehensive error handling and logging around the port assignment process. This could help in diagnosing issues if they persist after these changes.

  4. As the PR checklist indicates that verification across all platforms is pending, ensure that thorough testing is conducted on Windows, Linux, and macOS before merging these changes.

Given the complexity of the debugging process and the reported issues, it might be beneficial to implement more detailed logging throughout the launch process. This could help in identifying the exact point where hangs or other issues occur, especially when they're not easily reproducible across all environments.

bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (1)

Line range hint 474-479: LGTM: Logical control of GDB client group visibility.

The addition of the line to enable/disable the gdbClientGroup based on the fDoStartGdbServer selection is a good implementation. It ensures that the GDB client controls are only accessible when the GDB server is not started from the IDE, preventing potential conflicts.

Consider adding a comment explaining the logic behind this behavior for better code readability:

 gdbClientGroup.setEnabled(!fDoStartGdbServer.getSelection());
+// Enable GDB client controls only when the GDB server is not started from the IDE
 if (fDoStartGdbServer.getSelection())
 {
     fTargetIpAddress.setText(DefaultPreferences.REMOTE_IP_ADDRESS_LOCALHOST);
 }
bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)

28-29: Rename Variables for Clarity

For better readability and understanding, consider renaming the variables:

  • attempts to attemptsMade or attemptCount.
  • retryCount to maxRetries.

This will make it clearer that attemptsMade represents the number of attempts performed, and maxRetries indicates the maximum number of retry attempts allowed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5d4bfad and c30f2cb.

📒 Files selected for processing (3)
  • bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3 hunks)
🔇 Additional comments (5)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

150-155: Approve changes with suggestions for improvement

The changes align with the PR objectives by introducing a conditional port assignment based on whether the GDB server is to be started. This should help address the port handling issues mentioned.

However, I have a few suggestions to enhance this implementation:

  1. Please add a comment explaining the purpose of the Configuration.getDoStartGdbServer(config) condition. This will improve code readability and maintainability.

  2. The PR objectives mention a retry mechanism, but it's not visible in this code segment. Could you please verify if the retry logic is implemented in the PortChecker.getAvailablePort() method? If not, consider adding a retry mechanism here.

  3. As these changes might affect the behavior when the GDB server is not started from the IDE, please ensure that this scenario is thoroughly tested.

To verify the implementation of the retry mechanism and the getDoStartGdbServer method, please run the following script:

✅ Verification successful

[/run_scripts]


[/run_scripts]


Approve changes with confirmations

The changes align with the PR objectives by introducing a conditional port assignment based on whether the GDB server is to be started. The PortChecker.getAvailablePort method includes a retry mechanism, which addresses the port handling issues mentioned.

However, I have a suggestion to enhance this implementation:

  1. Please add a comment explaining the purpose of the Configuration.getDoStartGdbServer(config) condition. This will improve code readability and maintainability.
  2. As these changes might affect the behavior when the GDB server is not started from the IDE, please ensure that this scenario is thoroughly tested.
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the implementation of PortChecker and Configuration classes

# Test: Check PortChecker implementation
echo "Checking PortChecker implementation:"
rg --type java -A 10 "class PortChecker"

# Test: Check Configuration.getDoStartGdbServer implementation
echo "Checking Configuration.getDoStartGdbServer implementation:"
rg --type java -A 5 "getDoStartGdbServer"

Length of output: 6944


Script:

#!/bin/bash
# Description: Retrieve the implementation of getAvailablePort in PortChecker class

# Search for the getAvailablePort method in PortChecker.java
ast-grep --lang java --pattern 'public static int getAvailablePort($_)' bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java

Length of output: 498


Script:

#!/bin/bash
# Description: Locate the getAvailablePort method implementation in PortChecker.java using ripgrep

# Search for the getAvailablePort method definition
rg --type java -C 5 'public\s+static\s+int\s+getAvailablePort\s*\(' bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java

Length of output: 516

bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3)

112-112: LGTM: Good UI organization.

The addition of the gdbClientGroup variable is a positive change. It suggests an improvement in the organization of the GDB client controls, which can enhance the user interface's clarity and maintainability.


577-583: LGTM: Improved UI structure for GDB client controls.

The changes in the createGdbClientControls method effectively implement the grouping of GDB client controls. This reorganization enhances the UI structure and improves the visual separation of different control sets. The implementation follows good practices for creating SWT UI components.


Line range hint 1-1337: Overall improvements in GDB client UI organization and control.

The changes made to this file focus on enhancing the organization and control of GDB client-related UI elements. The introduction of the gdbClientGroup and its associated logic improves the structure of the user interface and provides better control over when GDB client options are accessible. These modifications are consistent with the existing code style and improve the overall user experience without introducing any apparent issues.

bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)

66-66: Remove the Unreachable Fallback Return Statement

The return statement at line 66 is unreachable because all paths return a value within the while loop. This fallback statement is unnecessary and can be safely removed to clean up the code.

Comment on lines 28 to 31
int attempts = 0;
int retryCount = 3;
long retryDelayMillis = 200;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider Making Retry Parameters Configurable

The retryCount and retryDelayMillis variables are currently hard-coded. To enhance flexibility and adaptability across different environments, consider making these parameters configurable, either through method arguments or configuration files.

Comment on lines 32 to 66
while (attempts <= retryCount)
{
return true;
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$
{
// If the socket opens, the port is in use
return false;
}
catch (IOException e)
{
// Port is unavailable, retrying if there are attempts left
if (attempts == retryCount)
{
// After exhausting all retries, return false
Logger.log("Port " + port + " is not available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return false; // Failure, port is still not available
}

attempts++;

// Log retry attempt
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

try
{
Thread.sleep(retryDelayMillis);
}
catch (InterruptedException interruptedException)
{
Thread.currentThread().interrupt(); // Restore interrupt status
Logger.log("Port availability check interrupted."); //$NON-NLS-1$
return false; // If interrupted, assume port unavailable and stop
}
}
}
return true; //Fallback not reachable
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix Logical Error in Port Availability Check

The isPortAvailable method currently has a logical error. It assumes that if it cannot establish a connection to the port after all retries, the port is unavailable. However, inability to connect to a port typically means the port is free and available for use. This method should return true when the port is available (i.e., no service is listening on it) and false when it's in use.

Apply this diff to correct the logic:

 while (attempts <= retryCount)
 {
     try (Socket ignored = new Socket("localhost", port))
     {
         // If the socket opens, the port is in use
         return false;
     }
     catch (IOException e)
     {
         // Port is available if we cannot connect
         if (attempts == retryCount)
         {
-            // After exhausting all retries, return false
-            Logger.log("Port " + port + " is not available after " + retryCount + " retries.");
-            return false; // Failure, port is still not available
+            // After exhausting all retries, return true
+            Logger.log("Port " + port + " is available after " + retryCount + " retries.");
+            return true; // Success, port is available
         }

         attempts++;

         // Log retry attempt
         Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms...");

         try
         {
             Thread.sleep(retryDelayMillis);
         }
         catch (InterruptedException interruptedException)
         {
             Thread.currentThread().interrupt(); // Restore interrupt status
             Logger.log("Port availability check interrupted.");
             return false; // If interrupted, assume port unavailable and stop
         }
     }
 }
- return true; //Fallback not reachable
+ return true; // Port is available after retries
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
while (attempts <= retryCount)
{
return true;
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$
{
// If the socket opens, the port is in use
return false;
}
catch (IOException e)
{
// Port is unavailable, retrying if there are attempts left
if (attempts == retryCount)
{
// After exhausting all retries, return false
Logger.log("Port " + port + " is not available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return false; // Failure, port is still not available
}
attempts++;
// Log retry attempt
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
try
{
Thread.sleep(retryDelayMillis);
}
catch (InterruptedException interruptedException)
{
Thread.currentThread().interrupt(); // Restore interrupt status
Logger.log("Port availability check interrupted."); //$NON-NLS-1$
return false; // If interrupted, assume port unavailable and stop
}
}
}
return true; //Fallback not reachable
while (attempts <= retryCount)
{
try (Socket ignored = new Socket("localhost", port)) //$NON-NLS-1$
{
// If the socket opens, the port is in use
return false;
}
catch (IOException e)
{
// Port is available if we cannot connect
if (attempts == retryCount)
{
// After exhausting all retries, return true
Logger.log("Port " + port + " is available after " + retryCount + " retries."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return true; // Success, port is available
}
attempts++;
// Log retry attempt
Logger.log("Attempt " + attempts + " failed, retrying in " + retryDelayMillis + " ms..."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
try
{
Thread.sleep(retryDelayMillis);
}
catch (InterruptedException interruptedException)
{
Thread.currentThread().interrupt(); // Restore interrupt status
Logger.log("Port availability check interrupted."); //$NON-NLS-1$
return false; // If interrupted, assume port unavailable and stop
}
}
}
return true; // Port is available after retries

@kolipakakondal
Copy link
Collaborator

Hi @alirana01 , thanks for the PR. I verified the debugging with the ESP32-Wrover-Kit, and it seems to be working fine. I no longer see the issue I reported earlier. However, I previously tested with the ESP32-C6, but my cable is currently missing, so I cannot verify it at this moment wit the c6.

@kolipakakondal
Copy link
Collaborator

Hi @AndriiFilippov Please test further

Copy link
Collaborator

@kolipakakondal kolipakakondal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@AndriiFilippov
Copy link
Collaborator

AndriiFilippov commented Oct 10, 2024

@alirana01 hi !

Tested under:
OS: Mac arm64
ESP-IDF: v5.3.1

  1. I have occupide 3333 port :
andriifilippov@Andriis-MacBook-Pro ~ % sudo lsof -i :3333
Password:
COMMAND   PID           USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
openocd 11100 andriifilippov    5u  IPv4 0x4a2957386a35d1ed      0t0  TCP localhost:dec-notes (LISTEN)
Python  65956 andriifilippov    4u  IPv6 0x4a29572a062c2c1d      0t0  TCP *:dec-notes (LISTEN)

and start debug with GDB
Screenshot 2024-10-10 at 13 42 13
and I just see endless amount of retries without switching to 3334 port.
Screenshot 2024-10-10 at 13 51 34

I am not able to stop the process.
Screenshot 2024-10-10 at 13 48 49

  1. I thought this logic is applicable to all ports, such as tcl and Telnet
Screenshot 2024-10-10 at 13 45 00 but it doesn't. If those ports are occupied:
andriifilippov@Andriis-MacBook-Pro ~ % sudo lsof -i :6666
Password:
COMMAND   PID           USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
openocd 11100 andriifilippov    6u  IPv4 0x4a2957386a35af6d      0t0  TCP localhost:6666 (LISTEN)

then it just throws error message, not looking for available port:

** Verify OK **
** Flashing done for partition_table/partition-table.bin in 460 ms **
** Total programming time 4491 ms **
** Resetting Target **
Info : JTAG tap: esp32.cpu0 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : JTAG tap: esp32.cpu1 tap/device found: 0x120034e5 (mfg: 0x272 (Tensilica), part: 0x2003, ver: 0x1)
Info : [esp32.cpu0] requesting target halt and executing a soft reset
Info : [esp32.cpu0] Debug controller was reset.
Info : [esp32.cpu0] Core was reset.
Info : [esp32.cpu0] Target halted, PC=0x500000CF, debug_reason=00000000
Info : [esp32.cpu0] Reset cause (3) - (Software core reset)
Info : [esp32.cpu1] requesting target halt and executing a soft reset
Info : [esp32.cpu0] Core was reset.
Info : [esp32.cpu1] Debug controller was reset.
Info : [esp32.cpu1] Core was reset.
0
Started by GNU MCU Eclipse
Error: couldn't bind tcl to socket on port 6666: Address already in use
Please refer to the troubleshooting guide below to identify the problem.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (6)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

150-153: Approve the condition, but suggest improvements

The addition of the condition to set the port number when the GDB server is started is a good step towards addressing the port handling logic issues. However, there are a few points to consider:

  1. The current implementation doesn't include a retry mechanism or port verification as mentioned in the PR summary. Consider implementing these to make the solution more robust.

  2. This change doesn't address the concerns raised by AndriiFilippov about endless retries and not switching to available ports. Consider adding logic to handle occupied ports and switch to available ones.

  3. It would be helpful to add a comment explaining the rationale behind this change, especially in relation to the issues it's meant to solve.

Consider expanding this implementation to include:

  • A retry mechanism with a maximum number of attempts
  • Port verification to ensure the chosen port is actually available
  • Logic to switch to an available port if the default is occupied

Example:

if (Configuration.getDoStartGdbServer(config)) {
    int port = DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT;
    int maxAttempts = 5;
    for (int attempt = 0; attempt < maxAttempts; attempt++) {
        if (PortChecker.isPortAvailable(port)) {
            config.setAttribute(IGDBJtagConstants.ATTR_PORT_NUMBER, port);
            break;
        }
        port++; // Try the next port
    }
    if (attempt == maxAttempts) {
        throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Unable to find an available port after " + maxAttempts + " attempts"));
    }
}

Also, consider adding a comment explaining the purpose of this block:

// Set the GDB server port when starting the server from the IDE.
// This ensures we use an available port and avoid conflicts.
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (1)

577-583: LGTM: GDB client group initialization.

The initialization and configuration of the gdbClientGroup are implemented correctly. The use of localized strings for the group title is good for internationalization.

For consistency with other parts of the code, consider extracting the string key to a constant:

-			gdbClientGroup.setText(Messages.getString("DebuggerTab.gdbSetupGroup_Text")); //$NON-NLS-1$
+			gdbClientGroup.setText(Messages.getString(Messages.DebuggerTab_gdbSetupGroup_Text));

And define the constant in the Messages class:

public static final String DebuggerTab_gdbSetupGroup_Text = "DebuggerTab.gdbSetupGroup_Text";

This approach would make it easier to manage and update string keys throughout the codebase.

bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (4)

31-31: Question the Necessity of setReuseAddress(true)

Calling serverSocket.setReuseAddress(true); after the socket has already been bound may not have any effect. Since the primary goal is to check if the port is available, and not to reuse the address, this line may be unnecessary. Removing it can simplify the code without impacting functionality.

Apply this diff to remove the unnecessary line:


Line range hint 43-47: Improve Handling When No Available Port Is Found

When the maximum number of attempts is reached without finding an available port, the method currently returns the last checked port, which may still be unavailable. This could lead to downstream failures. Consider throwing an exception or returning a sentinel value (e.g., -1) to clearly indicate that no available port was found.

Option 1: Throw an Exception

Modify the method to throw an IOException when no port is available:

 if (attemptsCount >= MAX_ATTEMPS)
 {
     Logger.log(Messages.PortChecker_AttemptLimitExceededMsg);
-    return port;
+    throw new IOException("No available ports found after " + MAX_ATTEMPS + " attempts.");
 }

Option 2: Return a Sentinel Value

Return -1 to indicate failure:

 if (attemptsCount >= MAX_ATTEMPS)
 {
     Logger.log(Messages.PortChecker_AttemptLimitExceededMsg);
-    return port;
+    return -1;
 }

Ensure that any calling methods handle this new behavior appropriately.


7-8: Remove Unused Import java.io.IOException

The import statement import java.io.IOException; is added but not used in the code. Removing unused imports can improve code readability and reduce clutter.

Apply this diff to remove the unused import:


29-32: Consider Binding to a Specific Local Address

If the application runs on a machine with multiple network interfaces, you might want to specify the local address to bind the ServerSocket to. This can prevent binding to unintended network interfaces.

Modify the ServerSocket instantiation to specify the local address if necessary:

try (ServerSocket serverSocket = new ServerSocket(port, backlog, InetAddress.getByName("localhost")))

Replace "localhost" with the appropriate local address if needed.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 2f779c4 and d0c28bd.

📒 Files selected for processing (4)
  • bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (2 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java (1 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1 hunks)
  • bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (3 hunks)
🧰 Additional context used
🔇 Additional comments (5)
bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/dsf/Launch.java (1)

Line range hint 1-253: Summary and Next Steps

The changes made to the Launch.java file partially address the port handling issues mentioned in the PR objectives. However, to fully resolve the problems and address the concerns raised in the comments, consider the following next steps:

  1. Implement a more robust port selection mechanism with retries and verification.
  2. Address the concerns about endless retries and switching to available ports.
  3. Ensure the solution works across all platforms (Windows, Linux, macOS) as mentioned in the PR summary.
  4. Add comprehensive error handling and logging to aid in debugging.
  5. Update the PR description to accurately reflect the implemented changes and any remaining tasks.

To improve the overall architecture and maintainability of the debugging process:

  1. Consider extracting the port selection logic into a separate utility class. This would make it easier to test and reuse across different parts of the codebase.
  2. Implement a configuration option to allow users to specify a range of acceptable ports. This would provide more flexibility and potentially avoid conflicts with other applications.
  3. Add telemetry (if not already present) to track the frequency and nature of port-related issues. This data could inform future improvements to the debugging process.

To ensure the changes work as expected across different scenarios, consider adding the following verification steps:

These verification steps will help ensure that the changes are consistent with the rest of the codebase and that no conflicting implementations exist.

bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/Configuration.java (1)

96-97: ⚠️ Potential issue

Consider maintaining consistency in port configuration retrieval

The change simplifies the GDB server port retrieval by using a default value directly. However, this approach differs from how other ports (Telnet and TCL) are handled in the same method, which still use configuration attributes.

Potential issues:

  1. Loss of flexibility: Users can no longer configure custom GDB server ports through launch configurations.
  2. Inconsistency: The GDB server port is handled differently from Telnet and TCL ports.
  3. Backward compatibility: Existing setups relying on custom GDB server port configurations may break.

Consider the following alternatives:

  1. Revert to using configuration attributes for consistency:
int port = PortChecker.getAvailablePort(configuration.getAttribute(ConfigurationAttributes.GDB_SERVER_GDB_PORT_NUMBER, DefaultPreferences.GDB_SERVER_GDB_PORT_NUMBER_DEFAULT));
  1. Apply the same simplification to Telnet and TCL ports for consistency.
  2. Add a comment explaining why the GDB server port is handled differently, if there's a specific reason for this change.

To ensure this change doesn't break existing configurations, please run the following script:

This will help identify any existing launch configurations that might be affected by this change.

bundles/com.espressif.idf.debug.gdbjtag.openocd/src/com/espressif/idf/debug/gdbjtag/openocd/ui/TabDebugger.java (2)

112-114: LGTM: New group for GDB client controls.

The addition of the gdbClientGroup variable improves the organization of the UI elements related to the GDB client settings.


Line range hint 474-481: LGTM: Improved control flow for GDB server/client interaction.

This change enhances the user experience by:

  1. Disabling the GDB client group when the GDB server is set to start locally, preventing conflicting configurations.
  2. Automatically setting the IP address to "localhost" when starting the GDB server locally, which is the correct behavior.

These modifications ensure a more intuitive and error-resistant UI.

bundles/com.espressif.idf.core/src/com/espressif/idf/core/util/PortChecker.java (1)

29-37: ⚠️ Potential issue

Handle InterruptedException in Port Checking Loop

In the getAvailablePort method, if an InterruptedException occurs during Thread.sleep(), the interrupt status is not restored or handled, which might lead to unexpected behavior. Ensure that the interrupt status is preserved or appropriately handled.

If the sleep is interrupted, consider restoring the interrupt status:

// Example code if sleep is used within the loop
try
{
    Thread.sleep(retryDelayMillis);
}
catch (InterruptedException interruptedException)
{
    Thread.currentThread().interrupt(); // Restore interrupt status
    Logger.log("Port checking was interrupted.");
    throw new IOException("Port checking was interrupted.", interruptedException);
}

Adjust the code according to your implementation needs.

Likely invalid or redundant comment.

Comment on lines +29 to +37
try (ServerSocket serverSocket = new ServerSocket(port))
{
return false;
serverSocket.setReuseAddress(true);
return true;
}
catch (Exception e)
{
return true;
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
return false;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Catch Specific Exceptions Instead of Generic Exception

Catching the generic Exception can mask other unintended exceptions and make debugging harder. It's better to catch the more specific IOException, which is the expected exception type for socket operations.

Apply this diff to catch IOException specifically:

-} catch (Exception e) {
+} catch (IOException e) {
    Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
    return false;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try (ServerSocket serverSocket = new ServerSocket(port))
{
return false;
serverSocket.setReuseAddress(true);
return true;
}
catch (Exception e)
{
return true;
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
return false;
try (ServerSocket serverSocket = new ServerSocket(port))
{
serverSocket.setReuseAddress(true);
return true;
}
catch (IOException e)
{
Logger.log("Port: " + port + " is not available"); //$NON-NLS-1$ //$NON-NLS-2$
return false;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants